# 1- 导包
from sklearn.datasets import load_iris
import seaborn as sns  # seaborn基于Matplotlib的一个画图工具
import matplotlib.pyplot as plt
import pandas as pd
from sklearn.model_selection import train_test_split  # 数据集划分工具
from sklearn.preprocessing import StandardScaler  # 标准化处理
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import accuracy_score  # 准确度评估方法


# EDA
def show_iris():
    # 2- 数据集的获取，在线下载
    iris_data = load_iris()
    print(type(iris_data), iris_data)

    print("-" * 50)

    # 特征数据->4列
    data_df = iris_data["data"]
    print(type(data_df), data_df)

    print("-" * 50)

    # 目标值数据->1列
    target_df = iris_data["target"]
    print(type(target_df), target_df)

    # 取四个特征字段名称
    feature_names = iris_data["feature_names"]
    print(feature_names)

    print("=" * 50)
    # 将4列特征数据与1列目标值拼成5列
    # target_df和data_df是numpy中的ndarray多维数据的类型。需要转成Pandas中的DataFrame
    pd_iris_df = pd.DataFrame(data_df, columns=feature_names)
    pd_iris_df["target"] = target_df
    print(pd_iris_df)
    print("=" * 50)

    # （了解）数据的其他信息
    print(iris_data["DESCR"])  # 数据集的介绍信息
    print(iris_data["target_names"])  # 三种花的名称

    # 以图形化（散点图）的形式展示数据
    # （理解）Seaborn实现方式
    """
        参数解释：
            pd_iris_df：需要展示的数据集
            x：横轴的字段名称（特征名称）
            y：纵轴的字段名称
            hue：对散点图进行颜色渲染
            fit_reg：是否展示拟合的曲线
    """
    sns.lmplot(pd_iris_df, x="sepal length (cm)", y="petal width (cm)", hue="target", fit_reg=False)

    # Matplot实现方法
    # 获取所有独一无二的目标类别
    # targets = pd_iris_df['target'].unique()

    # 为每个目标类别绘制散点图
    # 筛选出属于当前目标类别的数据
    # for target in targets:
    #     class_df = pd_iris_df[pd_iris_df['target'] == target]
    #     plt.scatter(class_df["sepal length (cm)"], class_df["petal width (cm)"], label=target)

    plt.show()

    return pd_iris_df


def ywh_show_info():
    data = load_iris()

    # 1.数据获取
    # 1.1 数据转换
    data_list = data['data']  # 获得特征数据
    data_target = data['target']  # 获得目标值

    # 获取到数据类型和数值
    # print(data)
    # print(data_list,type(data_list))
    # print(data_target,type(data_target))

    # 将ndarray转换成df对象，以便后面绘图方便查看特征与目标值的关联性
    # df_x = pd.DataFrame(data_list, columns=data['feature_names'])
    # df_y = pd.DataFrame(data_target)
    # df_x['target'] = df_y
    # print(df_x)
    #
    # sns.lmplot(data=df_x, x="sepal length (cm)", y="petal width (cm)", hue="target", fit_reg=False)
    # plt.show()

    # 2.特征工程

    # 2.1 特征提取 划分数据集（训练特征数据、测试特征数据；训练目标数据、测试目标数据）
    x_train, x_test, y_train, y_test = train_test_split(data_list, data_target, test_size=0.2)

    """
           fit：模型只对数据进行学习，对数据不做处理
           transform：只对数据进行处理，不学习
           fit_transform：模型先对数据进行学习，然后对数据进行处理

           为什么训练集必须使用fit_transform，而测试集必须使用transform？
               1- 特征预处理需要从训练集中学习数据的信息，目前是要学习到均值、标准差
               2- 然后基于学到的规律对训练集进行数据处理
               3- 拿着从训练集中学到的数据信息，对测试集进行处理。如果也是用fit_transform，那么会学到测试集的信息，这个是不允许的
       """
    # 2.1 特征预处理
    data_Standard = StandardScaler()  # 实例化标准化对象

    # 2.2.标准化数据(标准化特征数据)
    new_x_data = data_Standard.fit_transform(x_train)   # 必须使用fit_transform

    # 3.模型训练(分类算法)
    model = KNeighborsClassifier(n_neighbors=3)
    model.fit(new_x_data, y_train)

    # 4.模型评估
    # 4.1使用测试集对模型进行评估
    new_x_test = data_Standard.transform(x_test)

    # 4.2 预测数据（使用测试数据对训练好的模型进行评估，进一步获取准确率）
    y_test_predict = model.predict(new_x_test)  # 使用测试数据对模型进行预测
    print(f"预测结果准确度是：{accuracy_score(y_test, y_test_predict)}")

    # 5.预测未知数据
    unkown_x = [[5, 6, 7, 6]]
    new_unkown_x = data_Standard.transform(unkown_x)  # 对新传入的数据进行标准化，统一单位
    result = model.predict(new_unkown_x)
    print(f"预测结果是花的分类：{result}")

def ywh_show_info1(data_df):
    data = load_iris()

    # 1.数据获取
    # 1.1 数据转换
    data_list = data_df.iloc[:,0:4]  # 获得特征数据
    print(data_list,type(data_df))

    data_target = data_df.iloc[:,4]  # 获得目标值

    print(data_target, type(data_target))

    # # 获取到数据类型和数值
    # # print(data)
    # # print(data_list,type(data_list))
    # # print(data_target,type(data_target))
    #
    # 将ndarray转换成df对象，以便后面绘图方便查看特征与目标值的关联性
    # df_x = pd.DataFrame(data_list, columns=data['feature_names'])
    # df_y = pd.DataFrame(data_target)
    # df_x['target'] = df_y
    # print(df_x)
    #
    # sns.lmplot(data=df_x, x="sepal length (cm)", y="petal width (cm)", hue="target", fit_reg=False)
    # plt.show()
    #
    # 2.特征工程

    # 2.1 特征提取 划分数据集（训练特征数据、测试特征数据；训练目标数据、测试目标数据）
    x_train, x_test, y_train, y_test = train_test_split(data_list, data_target, test_size=0.2)

    """
           fit：模型只对数据进行学习，对数据不做处理
           transform：只对数据进行处理，不学习
           fit_transform：模型先对数据进行学习，然后对数据进行处理

           为什么训练集必须使用fit_transform，而测试集必须使用transform？
               1- 特征预处理需要从训练集中学习数据的信息，目前是要学习到均值、标准差
               2- 然后基于学到的规律对训练集进行数据处理
               3- 拿着从训练集中学到的数据信息，对测试集进行处理。如果也是用fit_transform，那么会学到测试集的信息，这个是不允许的
       """
    # 2.1 特征预处理
    data_Standard = StandardScaler()  # 实例化标准化对象

    # 2.2.标准化数据(标准化特征数据)
    new_x_data = data_Standard.fit_transform(x_train)   # 必须使用fit_transform

    # 3.模型训练(分类算法)
    model = KNeighborsClassifier(n_neighbors=3)
    model.fit(new_x_data, y_train)

    # 4.模型评估
    # 4.1使用测试集对模型进行评估
    new_x_test = data_Standard.transform(x_test)

    # 4.2 预测数据（使用测试数据对训练好的模型进行评估，进一步获取准确率）
    y_test_predict = model.predict(new_x_test)  # 使用测试数据对模型进行预测
    print(f"预测结果准确度是：{accuracy_score(y_test, y_test_predict)}")

    # 5.预测未知数据  数据类型要一致
    unkown_x = pd.DataFrame([[5, 6, 7, 6]],columns=["sepal length (cm)", "sepal width (cm)", "petal length (cm)", "petal width (cm)"])
    new_unkown_x = data_Standard.transform(unkown_x)  # 对新传入的数据进行标准化，统一单位
    result = model.predict(new_unkown_x)
    print(f"预测结果是花的分类：{result}")


if __name__ == '__main__':
    ywh_show_info()
    # data_df = show_iris()
    # ywh_show_info1(data_df)
